package controller

import (
	"encoding/json"
	"etcd/config"
	"etcd/e"
	"etcd/utile"
	"fmt"
	"github.com/gin-gonic/gin"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// 菜单结构体
type LogConfTreeList struct {
	Id     		string             	`json:"id"`
	ParentId    string             	`json:"parentId"`
	Title  		string          	`json:"title"`
	Last   		bool          		`json:"last"`
	Spread   	bool          		`json:"spread"`
	Children    []*LogConfTreeList 	`json:"children"`
}


func LogSysConf(c *gin.Context) {

	nodeId := c.DefaultPostForm("nodeId","/")
	parentId := c.DefaultPostForm("parentId","0")
	context := c.DefaultPostForm("context","/")
	//leaf := c.PostForm("leaf")
	level,_ := strconv.Atoi(c.DefaultPostForm("level","1"))
	//spread := c.PostForm("spread")

	etcd := utile.GetEtcd()
	keys,err := etcd.GetAllKeyByPrefix("/")
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	tree := map[string][]string{}
	if len(keys) > 0 {
		for _, v := range keys {
			strArr := strings.Split(v.Key,"/")
			fmt.Println(strArr)
			if len(strArr) < 0 || level > len(strArr) {
				continue
			}
			for i := 1;i<len(strArr);i++ {
				if i == 1 {
					if _,ok := tree[strArr[i]]; !ok {
						tree[strArr[i]] = []string{}
					}
					continue
				}
				tree[strArr[1]] = append(tree[strArr[1]],strArr[i])
			}
		}
	}
		fmt.Printf("%+v",tree)

	var treeList []*LogConfTreeList


	/**
	var data = [
	            {"id":"001", "title": "湖南省", "last": false, "parentId": "0",
	                "children":[
	                    {"id":"001001", "title": "长沙市", "last":true, "parentId": "001"},
	                    {"id":"001002", "title": "株洲市", "last":true, "parentId": "001"},
	                    {"id":"001003", "title": "湘潭市", "last":true, "parentId": "001"},
	                    {"id":"001004", "title": "衡阳市", "last":true, "parentId": "001"},
	                    {"id":"001005", "title": "郴州市", "last":true, "parentId": "001"}]},
	            {"id":"002", "title": "湖北省", "last": false, "parentId": "0",
	                "children":[
	                    {"id":"002001", "title": "武汉市", "last":true, "parentId": "002"},
	                    {"id":"002002", "title": "黄冈市", "last":true, "parentId": "002"},
	                    {"id":"002003", "title": "潜江市", "last":true, "parentId": "002"},
	                    {"id":"002004", "title": "荆州市", "last":true, "parentId": "002"},
	                    {"id":"002005", "title": "襄阳市", "last":true, "parentId": "002"}]},
	            ];
	 */

	node := &LogConfTreeList{
		Id:     nodeId,
		Title:  context,
		Last:   false,
		ParentId:   parentId,
	}

	if len(tree) > 0 {
		for k,v := range tree {

			child := &LogConfTreeList{
				Id:   k ,
				Title:  k,
				Last:   false,
				ParentId:   node.Id,
			}
			if  len(v) > 0 {
				for _, v2 := range v {
					child2 := &LogConfTreeList{
						Id:       v2,
						Title:    v2,
						Last:     true,
						ParentId: k,
					}
					child.Children = append(child.Children, child2)
				}
			} else {
				child.Last = true
			}
			node.Children = append(node.Children, child)
		}
	} else {
		node.Spread = true
	}
	treeList = append(treeList, node)
	jsonData := map[string]interface{}{
		"status" :map[string]string{
			"code":"0",
			"message":"ok",
		},
		"data": treeList,
	}
	c.JSON(http.StatusOK,jsonData)
}

func AddSysConfKey(c *gin.Context) {
	parentId := c.DefaultPostForm("parentId","/")
	context := c.PostForm("context")
	level,_ := strconv.Atoi(c.DefaultPostForm("level","2"))
	if level > 3 {
		Error(c,e.ERROR_PARAM,"配置目录最多3级")
		return
	}
	key := ""
	if level == 2 {
		key = parentId + context
	}
	if level == 3 {
		key = "/" + parentId + "/" + context
	}
	etcd := utile.GetEtcd()

	edata,err := etcd.Get(key)
	if edata.Key != "" {
		Error(c,e.ERROR_PARAM,"该配置已存在，请勿重复添加！")
		return
	}

	err = etcd.Set(key,"")
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	Success(c)
}

func EtcdDelKey(c *gin.Context) {
	key := c.DefaultPostForm("key","")
	if key == "" {
		Error(c,e.ERROR_PARAM)
		return
	}
	key = strings.Trim(key,",")
	etcd := utile.GetEtcd()
	if strings.Contains(key,",") {
		for _,v := range strings.Split(key,",") {
			if strings.EqualFold(v,"admin") {
				continue
			}
			err := etcd.Del(v)
			if err != nil {
				Error(c,e.ERROR_PARAM,err.Error())
				return
			}
		}
	} else {
		if strings.EqualFold(key,"admin") {
			Error(c,e.ERROR_PARAM,"无权限删除admin")
			return
		}
		err := etcd.Del(key)
		if err != nil {
			Error(c,e.ERROR_PARAM,err.Error())
			return
		}
	}
	Success(c)
}

func DelSysConfKey(c *gin.Context) {
	parentId := c.DefaultPostForm("parentId","/")
	context := c.PostForm("context")
	level,_ := strconv.Atoi(c.DefaultPostForm("level","2"))
	key := ""
	if level == 1 {
		Error(c,e.ERROR_PARAM,"顶级目录无法删除")
		return
	}
	if level == 2 {
		key = parentId + context
	}
	if level == 3 {
		key = "/" + parentId + "/" + context
	}
	etcd := utile.GetEtcd()
	err := etcd.Del(key)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	Success(c)
}

func GetSysConfInfoByKey(c *gin.Context) {
	parentId := c.DefaultPostForm("parentId","/")
	context := c.PostForm("context")
	level,_ := strconv.Atoi(c.DefaultPostForm("level","2"))
	if level > 3  || level < 2{
		return
	}
	key := "/" + parentId + "/" + context
	if level == 2 {
		key = "/" + context
	}
	etcd := utile.GetEtcd()
	edata,err := etcd.Get(key)
	sysconf := &config.Yml{}
	if edata.Val != "" {
		err = json.Unmarshal([]byte(edata.Val),sysconf)
		if err != nil {
			Error(c,e.ERROR_PARAM,err.Error())
			return
		}
	}
	Success(c,sysconf)
}


func EtcdSaveSysConf(c *gin.Context) {

	params := [][]string{
		{"name|名称", "string", "mixstring|has", "1,50"},
		{"host|IP地址", "string", "ip|has"},
		{"port|端口号", "string", "numberic|has"},
		{"role|角色", "string", "whitelist|has", "client,server"},
		{"log[info]|info日志", "string-no-filter", "mixstring|has", "1,50"},
		{"log[error]|error账号", "string-no-filter", "mixstring|has", "1,50"},
		{"log[debug]|debug账号", "string-no-filter", "mixstring|has", "1,50"},
		{"log[warn]|warn账号", "string-no-filter", "mixstring|has", "1,50"},
		{"username|账号", "string", "mixstring|has", "1,50"},
		{"password|密码", "string", "mixstring|has", "1,50"},
		{"dbname|数据库名称", "string", "mixstring|has", "1,50"},
		{"database|数据库名称", "int", "num|has", "0,99"},
		{"charset|字符集", "string", "whitelist|has", "utf8mb4,utf8"},
		{"timezone|时区", "string", "mixstring|has", "1,50"},
		{"driver|驱动", "string",  "whitelist|has", "mysql,sqlite,tcp"},
		{"prefix|前缀", "string", "mixstring|has", "1,50"},
		{"secret|密钥", "string", "mixstring|has", "1,50"},
		{"expire|过期时间", "int64", "num|has", "0,999999"},
		{"max_reconnect_num|最大重连次数", "int", "num|has", "0,999"},
		{"wait_reconnect_time|重连等待时间", "int", "num|has", "0,99999"},
		{"logfiles|日志文件路径", "string-no-filter"},
		{"server_host|smtp地址", "string", "mixstring|has","1,50"},
		{"server_port|端口", "string", "num|has", "80,99999"},
		{"toers|通知邮箱", "string"},
		{"form_key|配置key", "string", "required"},
		{"conf_slot|配置slot", "string", "required"},
	}
	data, err := formHelper.Params(params, c)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	var db config.Db
	db.Driver = data["driver"].(string)
	db.Host = data["host"].(string)
	db.Port = data["port"].(string)
	db.Dbname = data["dbname"].(string)
	db.Username = data["username"].(string)
	db.Password = data["password"].(string)
	db.Prefix = data["prefix"].(string)
	db.Charset = data["charset"].(string)
	db.TimeZone = data["timezone"].(string)

	var mq config.MQ
	mq.Host = data["host"].(string)
	mq.Port = data["port"].(string)
	mq.Username = data["username"].(string)
	mq.Password = data["password"].(string)
	mq.MaxReconnectNum = data["max_reconnect_num"].(int)
	mq.WaitReconnectTime = data["wait_reconnect_time"].(int)

	var es config.ES
	es.Host = data["host"].(string)
	es.Port = data["port"].(string)
	es.Username = data["username"].(string)
	es.Password = data["password"].(string)

	var redis config.Redis
	redis.Driver = data["driver"].(string)
	redis.Host = data["host"].(string)
	redis.Port = data["port"].(string)
	redis.Password = data["password"].(string)
	redis.Database = data["database"].(int)

	var jwt config.Jwt
	jwt.Secret = data["secret"].(string)
	jwt.Expire = time.Duration(data["expire"].(int64))

	var email config.Email
	email.ServerHost = data["server_host"].(string)
	email.ServerPort = data["server_port"].(string)
	email.UserName = data["username"].(string)
	email.Password = data["password"].(string)
	email.Toers = strings.Split(data["toers"].(string),",")

	var app config.App
	app.Name = data["name"].(string)
	app.Port = data["port"].(string)
	app.Role = data["role"].(string)
	app.Log = config.Log{data["log[info]"].(string),data["log[error]"].(string),data["log[debug]"].(string),data["log[warn]"].(string)}

	var etcd config.Etcd
	etcd.Host = data["host"].(string)
	etcd.Port = data["port"].(string)
	etcd.Username = data["username"].(string)
	etcd.Password = data["password"].(string)
	etcd.Prefix = data["prefix"].(string)

	logFiles := strings.Split(data["logfiles"].(string),",")

	key := c.PostForm("form_key")
	confSlot := c.PostForm("conf_slot")
	if confSlot == "" {
		Error(c,e.ERROR_PARAM)
		return
	}
	etcdCli := utile.GetEtcd()
	edata,err := etcdCli.Get(key)
	sysconf := &config.Yml{}
	if edata.Val != "" {
		err = json.Unmarshal([]byte(edata.Val),sysconf)
		if err != nil {
			Error(c,e.ERROR_PARAM,err.Error())
			return
		}
	}
	switch confSlot {
	case "app":sysconf.App = app
	case "database":sysconf.Db = db
	case "redis":sysconf.Redis = redis
	case "mq":sysconf.Mq = mq
	case "es":sysconf.Es = es
	case "jwt":sysconf.Jwt = jwt
	case "email":sysconf.Email = email
	case "etcd":sysconf.Etcd = etcd
	case "log":sysconf.LogFiles = logFiles
	}
	sysconfStr,_ := json.Marshal(sysconf)
	err = etcdCli.Set(key,string(sysconfStr))
	if err != nil {
		//fmt.Printf("保存失败：%s",err)
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	//fmt.Println("保存成功")
	Success(c)
}

func EtcdGetAllKeys(c *gin.Context)  {
	var formHelper utile.FormHelper
	params := [][]string{
		{"prefix|名称", "string"},
		{"page|名称", "int"},
		{"limit|名称", "int"},
	}
	data,err := formHelper.Params(params,c)
	prefix := data["prefix"].(string)
	page := data["page"].(int)
	pageSize := data["limit"].(int)
	etcd := utile.GetEtcd()
	keys ,err := etcd.GetAllKeyByPrefix(prefix)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	resData := keys
	if page > 0 && pageSize > 0 {
		total := int(math.Ceil(float64(len(keys)) / float64(pageSize)))
		start := (page-1) * pageSize
		end := start + pageSize
		if page >= total  {
			start = (total-1) * pageSize
			end = len(keys)
		}
		fmt.Printf("total:%v,start:%v,end:%v \n",total,start,end)
		resData = keys[start:end]
	}
	c.JSON(http.StatusOK, gin.H{"code":0,"data":resData,"count":len(keys)})
}

func EtcdGet(c *gin.Context)  {
	key := c.PostForm("key")
	etcd := utile.GetEtcd()
	data,_ := etcd.Get(key)
	Success(c,data)
}

func EtcdSet(c *gin.Context)  {
	params := [][]string{
		{"key|键","string","required|length","1,50"},
		{"value|值","string"},
		{"is_edit","int"},
	}
	form,err := formHelper.Params(params,c)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	key := form["key"].(string)
	value := form["value"].(string)
	isEdit := form["is_edit"].(int)
	etcd := utile.GetEtcd()
	data,_ := etcd.Get(key)
	if isEdit == 0 && data.Key != "" {
		Error(c,e.ERROR_PARAM,"该配置项已存在，请勿重复添加")
		return
	}
	err = etcd.Set(key,value)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	Success(c)
}

func EtcdResetPassword(c *gin.Context)  {
	params :=[][]string{
		{"old_password|旧密码","string","required|mixstring","6,18"},
		{"new_password|新密码","string","required|mixstring","6,18"},
		{"again_password|确认密码","string","required|same","new_password"},
	}
	form,err := formHelper.Params(params,c)
	oldpass := form["old_password"].(string)
	etcd := utile.GetEtcd()
	key := "admin"
	edata,_ := etcd.Get(key)
	if edata.Key == "" {
		Error(c,e.ERROR_PARAM,"未获取到配置数据，请联系管理员")
		return
	}
	if !strings.EqualFold(utile.Md5(oldpass),edata.Val) {
		Error(c,e.ERROR_PARAM,"密码错误")
		return
	}
	newpass := utile.Md5(form["new_password"].(string))
	err = etcd.Set(key,newpass)
	if err != nil {
		Error(c,e.ERROR_PARAM,err.Error())
		return
	}
	Success(c)
}